#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mhvi_search.h"

int main() {
    Config config;
    parse_config("builder_config.txt", &config); //解析索引配置
    parse_config("user_config.txt", &config);
    print_config(&config);

    //读取偏移数组
    Bin* offset_list = init_bin(config.offset_list_path, 4);
    // print_bin(offset_list, 0, 10, "uint32");

    //读取聚类中心
    Bin* cluster_center = init_bin(config.cluster_center_path, sizeof(CLUSTER_CENTER));
    // print_bin(cluster_center, 0, 1, "float");
    // print_bin(cluster_center, cluster_center->n-1, cluster_center->n, "float");

    //读取查询向量
    Bin* querys = init_bin(config.query_path, sizeof(FEATURE));
    // print_bin(querys, 0, 1, "float");

    //读取gt文件
    Bin* gts = init_bin(config.gt_path, sizeof(int));
    // print_bin(gts, 0, 1, "uint32");

    //读取last_layer
    LastLayer_Vectors* last_layer = create_last_layer_vectors(10);

    FILE* fp = fopen(config.last_layer_path, "rb");
    load_last_layer_vectors(last_layer, fp, 0, 10);
    // print_last_layer_vectors(last_layer);

    int recall_k = config.recall_k; //用于计算召回率的k
    int real_k = config.real_k; //维护多少个候选者
    int nprobe = config.last_layer_nprobe; //检索多少个聚类
    Request_Results* results = create_result();

    //进行向量查询
    //以下代码就是对每个请求的处理了，要做并行化的话从这里开始
    for (int i = 0; i < min(config.max_query,querys->n); i++) { //对于每个查询向量
        //初始化minlist
        struct minlist* nearest_centroid = create_minlist(nprobe); //保存每个请求最近的聚类中心
        struct minlist* nearest_vector = create_minlist(real_k); //保存每个请求最近的向量
        struct minlist_entry tmp_entry;

        Request_Result result = {0};
        //对于每个查询向量，首先计算出向量到每个聚类中心的距离，并维护在minlist中
        FEATURE* query = (FEATURE*)get_bin_line(querys, i); //获取查询向量
        for (int j = 0; j < cluster_center->n; j++) { //对于每个聚类中心
            CLUSTER_CENTER* center = (CLUSTER_CENTER*)get_bin_line(cluster_center, j);
            //计算到该聚类的距离
            float distance = l2_distance(query, center, DIM);
            //插入到minlist
            tmp_entry.id = j;
            tmp_entry.distance = distance;
            insert_minlist(nearest_centroid, &tmp_entry);
        }
        // print_minlist(nearest_centroid);

        //根据最近的聚类中心的id去偏移数组中获取该聚类中的所有向量
        struct minlist_entry* centroid = get_mins(nearest_centroid);
        for(int j=0; j<nprobe; j++){ //对于每个最近的聚类中心
            int index_start = *(int*)get_bin_line(offset_list, centroid[j].id); //获取这个聚类的偏移量
            int index_end = *(int*)get_bin_line(offset_list, centroid[j].id+1);
            // printf("centroid:%d index_start=%d, index_end=%d, length=%d\n", centroid[j].id, index_start, index_end, index_end-index_start);

            LastLayer_Vectors* vectors = create_last_layer_vectors(index_end-index_start);
            int ret_count = load_last_layer_vectors(vectors, fp, index_start, index_end-index_start);
            result.io_count++;
            result.io_length += index_end-index_start;
            for(int k=0; k<ret_count; k++){ //对于每个读上来的向量
                LastLayer_Vector* vector = &(vectors->vectors[k]);
                //计算到该向量的距离
                float distance = l2_distance(query, vector->features, DIM);
                //插入到minlist
                tmp_entry.id = vector->id;
                tmp_entry.distance = distance;
                insert_minlist(nearest_vector, &tmp_entry);
            }
            destroy_last_layer_vectors(vectors);
        }
        //打印最终结果
        // print_minlist(nearest_vector);
        //计算召回率
        float recall = recall_rate(nearest_vector, gts, i, recall_k)*100;
        // printf("recall_rate=%7.4f %% \n", recall);

        result.id = i;
        result.recall_rate = recall;
        insert_result(results, &result);

        destory_minlist(nearest_centroid);
        destory_minlist(nearest_vector);
    }

    print_result(results, &config);

    //销毁对象
    fclose(fp);
    destroy_bin(offset_list);
    destroy_bin(cluster_center);
    destroy_bin(querys);
    destroy_bin(gts);
    destroy_last_layer_vectors(last_layer);
    destroy_result(results);

    return 0;
}